/**
 * @file	voice.c
 * @author	chipsea
 * @brief	
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */

/*******************************************************************************
* @file   voice.c
* @brief  Contains all functions support for adc driver
* @version  0.0
* @date   16. Jun. 2018
* @author qing.han
*
.
*
*******************************************************************************/
#include "rom_sym_def.h"
#include "error.h"
#include "cst92f2x.h"
#include "gpio.h"
#include "pwrmgr.h"
#include "clock.h"
#include "adc.h"
#include <string.h>
#include "log.h"
#include "voice.h"
#include "jump_function.h"

static voice_Ctx_t mVoiceCtx;

static uint32_t voice_data[HALF_VOICE_WORD_SIZE];

// Enable voice core
void hal_voice_enable(void)
{
	hal_clk_gate_enable(MOD_ADCC);
	subWriteReg(0x40050000,0,0,1);
}

// Disable voice core
void hal_voice_disable(void)
{
	hal_clk_gate_disable(MOD_ADCC);
	subWriteReg(0x40050000,0,0,0);
}

// Select DMIC
void hal_voice_dmic_mode(void)
{
  subWriteReg(0x4005000c,0,0,1);
}

// Select AMIC
void hal_voice_amic_mode(void)
{
  subWriteReg(0x4005000c,0,0,0);
	subWriteReg(0x4000f048,7,5,0);		//Connect ADC to PGA
	subWriteReg(0x4000f07c,4,4,1);
	subWriteReg(0x4000f07c,0,0,1);
	subWriteReg(0x4000F000 + 0x7c,2,1,HAL_ADC_CLOCK_320K);
}

// Open a GPIO pin for DMIC
void hal_voice_dmic_open(GpioPin_t dmicDataPin, GpioPin_t dmicClkPin)
{
	HalGpioFmuxConfig(dmicDataPin, (Fmux_Type_e)FMUX_ADCC);
	HalGpioFmuxConfig(dmicClkPin, (Fmux_Type_e)FMUX_CLK1P28M);
}

// Set PGA gain for AMIC
void hal_voice_amic_gain(uint8_t amicGain)
{
	uint8_t pgaGain1;
	uint8_t pgaGain2;
	
	if (amicGain > 14)
		amicGain = 14;
	
	if (amicGain > 8) {
		pgaGain1 = 2;
		pgaGain2 = amicGain - 8;
	}
	else if (amicGain > 4) {
		pgaGain1 = 1;
		pgaGain2 = amicGain - 4;
	}
	else {
		pgaGain1 = 0;
		pgaGain2 = amicGain;
	}
	subWriteReg(0x4000f048,18,17,(uint32_t)pgaGain1);
	subWriteReg(0x4000f048,21,19,(uint32_t)pgaGain2);
}

// Set voice process gain
void hal_voice_gain(uint8_t voiceGain)
{
  subWriteReg(0x4005000c,22,16,(uint32_t)voiceGain);
}

// Set voice encoding mode
void hal_voice_encode(VOICE_ENCODE_t voiceEncodeMode)
{
  subWriteReg(0x4005000c,13,12,voiceEncodeMode);
}

// Set voice data rate
void hal_voice_rate(VOICE_RATE_t voiceRate)
{
  subWriteReg(0x4005000c,9,8,voiceRate);
}

// INTERNAL: Set voice notch filter config
static void set_voice_notch(VOICE_NOTCH_t voiceNotch)
{
  subWriteReg(0x4005000c,3,2,voiceNotch);
}

// INTERNAL: Set voice data polarity
static void set_voice_polarity(VOICE_POLARITY_t voicePolarity)
{
  subWriteReg(0x4005000c,1,1,voicePolarity);
}

// Enable voice auto-mute
void hal_voice_amute_on(void)
{
	subWriteReg(0x40050014,0,0,0);
}

// Disable voice auto-mute
void hal_voice_amute_off(void)
{
	subWriteReg(0x40050014,0,0,1);
}

// INTERNAL: Set voice auto-mute configurations
static void set_voice_amute_cfg(
	uint16_t amutGainMax, 
	uint8_t amutGainBwMax, 
	uint8_t amutGdut, 
	uint8_t amutGst2, 
	uint8_t amutGst1, 
	uint16_t amutLvl2, 
	uint16_t amutLvl1, 
	uint8_t amutAlvl,
	uint8_t amutBeta,
	uint8_t amutWinl)
{
	subWriteReg(0x40050010,30,20,(uint32_t)amutGainMax);
	subWriteReg(0x40050010,19,16,(uint32_t)amutGainBwMax);
	subWriteReg(0x40050010,13,8,(uint32_t)amutGdut);
	subWriteReg(0x40050010,7,4,(uint32_t)amutGst2);
	subWriteReg(0x40050010,3,0,(uint32_t)amutGst1);
	subWriteReg(0x40050014,30,20,(uint32_t)amutLvl2);
	subWriteReg(0x40050014,18,8,(uint32_t)amutLvl1);
	subWriteReg(0x40050018,15,8,(uint32_t)amutAlvl);
	subWriteReg(0x40050018,6,4,(uint32_t)amutBeta);
	subWriteReg(0x40050018,3,0,(uint32_t)amutWinl);
}



/**************************************************************************************
 * @fn          hal_VOICE_IRQHandler
 *
 * @brief       This function process for adc interrupt
 *
 * input parameters
 *
 * @param       None.
 *
 * output parameters
 *
 * @param       None.
 *
 * @return      None.
 **************************************************************************************/
void __attribute__((used)) HalAdcIRQHandler(void)
{
//  uint32_t voice_data[HALF_VOICE_SAMPLE_SIZE];
	volatile uint32_t voice_int_status = GET_IRQ_STATUS;
//	LOG("Voice interrupt processing\n");
  MASK_VOICE_INT;
	
	if (voice_int_status & BIT(8)) {
		int n;
		for (n = 0; n < HALF_VOICE_WORD_SIZE; n++) {
			voice_data[n] = (uint32_t)(read_reg(VOICE_BASE + n * 4));
		}
		CLEAR_VOICE_HALF_INT;
		while (IS_CLAER_VOICE_HALF_INT) {}
//    if(mVoiceCtx.enable == FALSE)
//			continue;
		if (mVoiceCtx.evt_handler) {
			voice_Evt_t evt;
			evt.type = HAL_VOICE_EVT_DATA;
			evt.data = voice_data;
			evt.size = HALF_VOICE_WORD_SIZE;
			mVoiceCtx.evt_handler(&evt);
//			LOG("Voice memory half full interrupt processing completed\n");
		}
	}
	if (voice_int_status & BIT(9)) {
		int n;
		for (n = 0; n < HALF_VOICE_WORD_SIZE; n++) {
			voice_data[n] = (uint32_t)(read_reg(VOICE_MID_BASE + n * 4));
		}
		CLEAR_VOICE_FULL_INT;
		while (IS_CLAER_VOICE_FULL_INT) {}
//    if(mVoiceCtx.enable == FALSE)
//			continue;
		if (mVoiceCtx.evt_handler) {
			voice_Evt_t evt;
			evt.type = HAL_VOICE_EVT_DATA;
			evt.data = voice_data;
			evt.size = HALF_VOICE_WORD_SIZE;
			mVoiceCtx.evt_handler(&evt);
//			LOG("Voice memory full interrupt processing completed\n");
		}
	}
	
  ENABLE_VOICE_INT;
}

/**************************************************************************************
 * @fn          hal_voice_init
 *
 * @brief       This function process for adc initial
 *
 * input parameters
 *
 * @param       ADC_MODE_e mode: adc sample mode select;1:SAM_MANNUAL(mannual mode),0:SAM_AUTO(auto mode)
 *              ADC_CH_e adc_pin: adc pin select;ADC_CH0~ADC_CH7 and ADC_CH_VOICE
 *              ADC_SEMODE_e semode: signle-ended mode negative side enable; 1:SINGLE_END(single-ended mode) 0:DIFF(Differentail mode)
 *              IO_CONTROL_e amplitude: input signal amplitude, 0:BELOW_1V,1:UP_1V
 *
 * output parameters
 *
 * @param       None.
 *
 * @return      None.
 **************************************************************************************/
void hal_voice_init(void) {
  hal_pwrmgr_register(MOD_ADCC,NULL,NULL);
	hal_pwrmgr_register(MOD_VOC,NULL,NULL);
  memset(&mVoiceCtx, 0, sizeof(mVoiceCtx));;
}

int hal_voice_config(voice_Cfg_t cfg, voice_Hdl_t evt_handler)
{
  if(mVoiceCtx.enable)
    return ERR_BUSY;
	
  if(evt_handler == NULL)
   return ERR_INVALID_PARAM;
	
	hal_clk_gate_enable(MOD_ADCC);//enable I2C clk gated

	mVoiceCtx.evt_handler = evt_handler; //evt_handler;
		
  if(cfg.voiceSelAmicDmic) {
		hal_voice_dmic_mode();
		hal_voice_dmic_open(cfg.dmicDataPin, cfg.dmicClkPin);		
	}
	else {
		hal_voice_amic_mode();
		hal_voice_amic_gain(cfg.amicGain);
		HalGpioAnalogConfig(P15,Bit_ENABLE);//config micphone bias
	}
	
	hal_voice_gain(cfg.voiceGain);
	hal_voice_encode(cfg.voiceEncodeMode);
	hal_voice_rate(cfg.voiceRate);
	
	set_voice_notch(VOICE_NOTCH_1);
	set_voice_polarity(VOICE_POLARITY_POS);
	
	if(cfg.voiceAutoMuteOnOff) {
		hal_voice_amute_off();
	}
	else {
		hal_voice_amute_on();
	}
	
	set_voice_amute_cfg(64, 6, 9, 0, 1, 55, 10, 48, 3, 10);
	
  mVoiceCtx.cfg = cfg;

  
  //CLK_1P28M_ENABLE;
  AP_PCRM->CLKSEL |= BIT(6);
  
	//ENABLE_XTAL_OUTPUT;         //enable xtal 16M output,generate the 32M dll clock
  AP_PCRM->CLKHF_CTL0 |= BIT(18);
  
	//ENABLE_DLL;                  //enable DLL
  AP_PCRM->CLKHF_CTL1 |= BIT(7);
	
	//ADC_DBLE_CLOCK_DISABLE;      //disable double 32M clock,we are now use 32M clock,should enable bit<13>, diable bit<21>
  AP_PCRM->CLKHF_CTL1 &= ~BIT(21);
	
	//ADC_CLOCK_ENABLE;            //adc clock enbale,always use clk_32M
  AP_PCRM->CLKHF_CTL1 |= BIT(13);

  //subWriteReg(0x4000f07c,4,4,1);    //set adc mode,1:mannual,0:auto mode
  AP_PCRM->ADC_CTL4 |= BIT(4);
	//*(volatile unsigned int *) 0x4000f040=0x5014B820;
	//*(volatile unsigned int *) 0x4000f044=0x019028b0;
  //*(volatile unsigned int *) 0x4000f048=0x0000014b;
	
//	hal_pwrmgr_register(MOD_ADCC,NULL,NULL);
//  hal_pwrmgr_register(MOD_VOC,NULL,NULL);
	
	return SUCCESS;
}

int hal_voice_start(void)
{
  hal_clk_gate_enable(MOD_ADCC);
	
	mVoiceCtx.enable = TRUE;
  hal_pwrmgr_lock(MOD_ADCC);
	hal_pwrmgr_lock(MOD_VOC);
	

  if (mVoiceCtx.cfg.voiceSelAmicDmic) {
	}		
	else {
		AP_PCRM->ANA_CTL |= BIT(16);	//Power on PGA
		AP_PCRM->ANA_CTL |= BIT(3);		//Power on ADC
		AP_PCRM->ANA_CTL |= BIT(0);
		AP_PCRM->ANA_CTL |= BIT(23);
  }


  NVIC_SetPriority((IRQn_Type)ADCC_IRQn, IRQ_PRIO_HAL);//teddy add 20190121	
  NVIC_EnableIRQ((IRQn_Type)ADCC_IRQn);
  
  
	//Enable voice core
	hal_voice_enable();
	
	JUMP_FUNCTION(V29_IRQ_HANDLER)                  =   (uint32_t)&HalAdcIRQHandler;
	//Enable VOICE IRQ
	ENABLE_VOICE_INT;
	
  return SUCCESS;

}

int hal_voice_stop(void)
{
	MASK_VOICE_INT;

	//Disable voice core
	hal_voice_disable();

	if (mVoiceCtx.cfg.voiceSelAmicDmic) {
	}		
	else {
		AP_PCRM->ANA_CTL &= ~BIT(16);	//Power off PGA
	}

	//Enable sleep
	hal_pwrmgr_unlock(MOD_VOC);
	hal_pwrmgr_unlock(MOD_ADCC);

	JUMP_FUNCTION(V29_IRQ_HANDLER)                  = 0;
	mVoiceCtx.enable = FALSE;
	
	return 0;
}

int hal_voice_clear(void)
{
  //MASK_VOICE_INT;
  MASK_VOICE_INT;
  
  NVIC_DisableIRQ((IRQn_Type)ADCC_IRQn);
	
	if (mVoiceCtx.cfg.voiceSelAmicDmic) {
		hal_gpioin_disable(mVoiceCtx.cfg.dmicDataPin);
		hal_gpioin_disable(mVoiceCtx.cfg.dmicClkPin);
  }
	else	{
	}
	
  //clk_gate_disable(MOD_ADCC);//disable I2C clk gated

	memset(&mVoiceCtx, 0, sizeof(mVoiceCtx));
	
  //enableSleep();
  hal_pwrmgr_unlock(MOD_VOC);
	
	return 0;
}
